#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <semaphore.h>
#include "type.h"
#include "buf_misc.h"
#include "cli_main.h"

static TIMER_QUEUE *timer_queue_free = NULL;
static TIMER_QUEUE *timer_queue = NULL;
static I8 *timer_queue_tmp = NULL;
static BUF_HDR *buf_head = NULL;
static sem_t sem_buf;
static U32 buff_start = 0;
static U32 buff_end = 0;

I8 *up_cache;
I8 *up_back;
I8 *up_tmp;
I8 *upCurrentData;
I8 *config_file_buf = NULL;

//#undef IF_APP_UNIT_TEST
#ifdef IF_APP_UNIT_TEST
static I32 buf_used = 0;
#endif

static I32 timer_queue_init(void)
{
    I32 i;
	
    timer_queue_tmp = malloc(TIMER_QUEUE_SIZE * sizeof(TIMER_QUEUE));
    if (timer_queue_tmp == NULL) {
        cli_app_log(CLI_LOG_CRITICAL, "ERROR allocate timer queue\n");
        return -1;
    }

    timer_queue_free = (TIMER_QUEUE *) timer_queue_tmp;

    for (i = 0; i < (TIMER_QUEUE_SIZE - 1); i++) {
        timer_queue_free[i].next = (TIMER_QUEUE *) (&timer_queue_free[i + 1]);
    }
	
    timer_queue_free[i].next = NULL;
    return 0;
}

static I32 timer_queue_exit(void)
{

    free(timer_queue_tmp);
    timer_queue_free = NULL;
    timer_queue = NULL;

    return 0;
}

I32 timer_queue_add(I32 timeout, I32 id, I32 p_f, I32 p_s, I32 p_l, I8 * p_d)
{
    TIMER_QUEUE *tmp;
    I32 ret = -1;
	
    if (timer_queue_free != NULL) {
		
        tmp = timer_queue_free;
        timer_queue_free = timer_queue_free->next;
        tmp->timeout = timeout;
        tmp->id = id;
        tmp->p_from = p_f;
        tmp->p_socket = p_s;
        tmp->p_len = p_l;
        tmp->p_data = p_d;
        tmp->next = timer_queue;
        timer_queue = tmp;
		
        ret = 0;
    }
	
    return ret;
}

I32 timer_queue_find(I32 id)
{
    TIMER_QUEUE *tmp = timer_queue;
    TIMER_QUEUE *tmp1;
    I32 ret = -1;

    if (tmp == NULL) {
        return ret;
    }
	
    if (tmp->id == id) {
        ret = 0;
    } else {
        while (tmp->next != NULL) {
            tmp1 = tmp->next;
            if (tmp1->id == id) {
                ret = 0;
                break;
            } else {
                tmp = tmp->next;
            }
        }
    }
	
    return ret;

}

I32 timer_queue_del(I32 id)
{
    TIMER_QUEUE *tmp = timer_queue;
    TIMER_QUEUE *tmp1;
    I32 ret = -1;

    if (tmp == NULL) {
		return ret;
    }
	
    if (tmp->id == id) {
		
        timer_queue = tmp->next;
        tmp->next = timer_queue_free;
        timer_queue_free = tmp;
		
        ret = 0;
    } else {
    
        while (tmp->next != NULL) {
            tmp1 = tmp->next;
            if (tmp1->id == id) {
				
                tmp->next = tmp1->next;
                tmp1->next = timer_queue_free;
                timer_queue_free = tmp1;
				
                ret = 0;
                break;
            } else {
            
                tmp = tmp->next;
            }
        }
    }
	
    return ret;
}

I32 timer_queue_update(I32 id, I32 timeout)
{
    TIMER_QUEUE *tmp = timer_queue;
    TIMER_QUEUE *tmp1;
    I32 ret = -1;

    if (tmp == NULL) {
		return ret;
    }
	
    if (tmp->id == id) {
        tmp->timeout = timeout;

		ret = 0;
    } else {
        while (tmp->next != NULL) {
            tmp1 = tmp->next;
            if (tmp1->id == id) {
                tmp1->timeout = timeout;
				
                ret = 0;
                break;
            } else {
                tmp = tmp->next;
            }
        }
    }
	
    return ret;
}

I32 timer_queue_timeout(I32 tick)
{
    TIMER_QUEUE *tmp1, *tmp2;
    tmp1 = timer_queue;
    if (tmp1 == NULL) {
        return 0;
    }

    tmp2 = tmp1->next;
    while (tmp2 != NULL) {
		
        tmp2->timeout -= tick;
        if (tmp2->timeout <= 0) {
			
            if (tmp2->id == TIMER_ID_PENDING_CMD) {
                event_queue_add(tmp2->p_from, tmp2->p_socket, tmp2->p_len, tmp2->p_data);
            } else {
                event_queue_add(IF_APP_EVENT_TIMEOUT, tmp2->id, 0, NULL);
            }
			
            tmp1->next = tmp2->next;
            tmp2->next = timer_queue_free;
            timer_queue_free = tmp2;
            tmp2 = tmp1->next;
        } else {
        
            tmp1 = tmp2;
            tmp2 = tmp2->next;
        }
    }
	
    tmp1 = timer_queue;
    tmp1->timeout -= tick;
    if (tmp1->timeout <= 0) {
		
        if (tmp1->id == TIMER_ID_PENDING_CMD) {
            event_queue_add(tmp1->p_from, tmp1->p_socket, tmp1->p_len,tmp1->p_data);
        } else {
            event_queue_add(IF_APP_EVENT_TIMEOUT, tmp1->id, 0, NULL);
        }
		
        timer_queue = tmp1->next;
        tmp1->next = timer_queue_free;
        timer_queue_free = tmp1;
    }
	
    return 0;
}

#ifdef MODULE_TEST 
static I32 buffer_get_free_num()
{
    BUF_HDR *tmp;
    I32 i = 0;
    tmp = buf_head;
    while (tmp != NULL) {
        i++;
        tmp = tmp->next;
    }
    return i;
}
#endif

I8 *buffer_allocate()
{
    I8 *tmp = NULL;
	
    sem_wait(&sem_buf);
	
    if (buf_head != NULL) {
        tmp = (I8 *) buf_head;
        buf_head = buf_head->next;
    }

#ifdef MODULE_TEST
    if (tmp != NULL) {
        buf_used++;
        cli_app_log(CLI_LOG_DEFAULT, 
					"allocated %08x, used %d, free %d\n",
                    (U32) tmp, buf_used, buffer_get_free_num());
    }
#endif

    sem_post(&sem_buf);

    return tmp;
}

void buffer_free(I8 * buf)
{
    BUF_HDR *tmp;

    if (buf == NULL) {
        cli_app_log(CLI_LOG_ERROR, 
					"WARNING: try to free NULL to pool\n");
        return;
    }
	
    if (((U32) buf < buff_start) || ((U32) buf > buff_end)) {
        cli_app_log(CLI_LOG_ERROR, 
					"WARNING: try to free %08x to pool\n",
                    (U32) buf);
        return;
    }

    tmp = (BUF_HDR *) (buf - (((U32) buf - buff_start) % BUFFER_BLOCK_SIZE));
    sem_wait(&sem_buf);
    tmp->next = buf_head;
    buf_head = tmp;

#ifdef MODULE_TEST
    buf_used--;
    cli_app_log(CLI_LOG_DEFAULT, 
				"freeed %08x,%08x, used %d, free %d\n",
                (U32) buf, (U32) tmp, buf_used, buffer_get_free_num());
#endif

    sem_post(&sem_buf);

    return;
}

I32 buf_msic_init(void)
{
    I8 *buf;
    I32 i;
    up_cache = malloc(BUF_SIZE_UPDATA);
    if (up_cache == NULL) {
        cli_app_log(CLI_LOG_CRITICAL, 
					"ERROR allocate buffer for update cache\n");
        return -1;
    }

	up_back = malloc(BUF_SIZE_UPDATA);
    if (up_back == NULL) {
        cli_app_log(CLI_LOG_CRITICAL, 
					"ERROR allocate buffer for update backup\n");
        return -1;
    }
    up_tmp = malloc(BUF_SIZE_UPDATA);
    if (up_tmp == NULL) {
        cli_app_log(CLI_LOG_CRITICAL, 
					"ERROR allocate buffer for update tmp\n");
        return -1;
    }
   
    upCurrentData = malloc(BUF_SIZE_UPDATA);
    if (upCurrentData == NULL) {
        cli_app_log(CLI_LOG_CRITICAL, 
					"ERROR allocate buffer for upCurrentData\n");
        return -1;
    }
	
    config_file_buf = malloc(FILE_BUF_LEN);
    if (config_file_buf == NULL) {
        cli_app_log(CLI_LOG_CRITICAL, 
					"ERROR allocate buffer for config file\n");
        return -1;
    }
	
    buf = malloc(BUFFER_BLOCK_SIZE * BUFFER_SIZE);
    buff_start = (U32) buf;
    buff_end = buff_start + (BUFFER_BLOCK_SIZE * BUFFER_SIZE);

    cli_app_log(CLI_LOG_INFO, 
				"buf init, buf start %08x, buf end %08x\n",
                buff_start, buff_end);
	
    if (buf == NULL) {
        cli_app_log(CLI_LOG_CRITICAL, 
					"ERROR allocate buffer for internal buffer\n");
        return -1;
    }
	
    buf_head = (BUF_HDR *) buf;
	
    for (i = 0; i < (BUFFER_SIZE - 1); i++){
        ((BUF_HDR *) (buf + (i * BUFFER_BLOCK_SIZE)))->next = (BUF_HDR *) (buf + ((i + 1) * BUFFER_BLOCK_SIZE));
    }
	
    ((BUF_HDR *) (buf + (i * BUFFER_BLOCK_SIZE)))->next = NULL;

    sem_init(&sem_buf, 0, 1);

    timer_queue_init();
    return 0;
}

I32 buf_msic_exit(void)
{
    free(up_cache);
    free(up_tmp);
    free(config_file_buf);
	
    sem_wait(&sem_buf);
    free(buf_head);
    buf_head = NULL;
    sem_post(&sem_buf);
	
    sem_destroy(&sem_buf);
    timer_queue_exit();

    return 0;
}

void buffer_dump_log(I8* buf, I32 buf_len, I32 level)
{
    I8 *buf_log;

    if( (buf_log = buffer_allocate()) != NULL )
    {
        I32 len = 0;
        int i;
        for( i = 0; i < buf_len / 2; ++i )
        {
            len += sprintf(buf_log + len, "%04x ", *(U16 *)(buf + (i * 2)));
        }

        len += sprintf(buf_log + len, "\n");
        buf_log[len] = 0;

        cli_app_log(level, buf_log);

        buffer_free(buf_log);
    } 
    else 
    {
        cli_app_log(CLI_LOG_CRITICAL, "cli_main: get buffer fail\n");
    }
}
